package com.neu.wms.security;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.neu.wms.util.JwtUtils;
import com.neu.wms.util.RedisCache;
import com.neu.wms.util.ResultVo;
import com.neu.wms.util.ServerCode;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author DAIJUNQIANG
 * @version 1.0
 * @description 当用户登录过后，我们验证每个请求中的token是否有效，如果有效则放行（认为已经登录），无效则让用户重新登录
 * @date 2023/7/4 11:50
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


	@Autowired
	private RedisCache redisCache;
	@Autowired
	private JwtUtils jwtUtils;

	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
		String token = request.getHeader("Authorization");

		if (StrUtil.isEmptyOrUndefined(token)) {//用户登录时还没有token信息，直接放行，请求LoginFilter
			filterChain.doFilter(request, response);
			return;
		}

		Claims claims = jwtUtils.getClaimsByToken(token);
		if (claims == null) {//token不为空，但是不能当前系统发出的
			String s = new ObjectMapper().writeValueAsString(ResultVo.error(ServerCode.ERR_JWT_PARSE));
			response.getWriter().println(s);
			return;
		}

		boolean tokenExpired = jwtUtils.isTokenExpired(claims);
		if (tokenExpired) {//如果过期，则重新登录
			String s = new ObjectMapper().writeValueAsString(ResultVo.error("登录失效了，请重新登录"));
			response.getWriter().println(s);
			return;
		}


		//如果能走到这里，说明token有效
		String userName = claims.getSubject();//取得用户名信息
		//走到这里都要查询数据库，效率低下，所以我们在登录时，就把用户信息放到redis中，这里直接从redis中取
		LoginUser userDetails = redisCache.getCacheObject("neu:" + userName);
		//UserDetails userDetails = userDetailsService.loadUserByUsername(userName);//调用后台接口，得到用户信息及权限等


		UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
		//把认证信息放到SecurityContext中，将来做鉴权时使用
		SecurityContextHolder.getContext().setAuthentication(authenticationToken);
		filterChain.doFilter(request, response);
	}
}
